#include "MD5Checksum.h"

void Update(PMD5Checksum This,BYTE* Input,	ULONG nInputLen );
char* Final(PMD5Checksum This,char* Buff);
void  DWordToByte(BYTE* Output, DWORD* Input, UINT nLength );
void CMD5ChecksumIni(PMD5Checksum This);
void Transform(PMD5Checksum This,BYTE Block[64]);
void ByteToDWord(DWORD* Output, BYTE* Input, UINT nLength);
void II( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T);
void HH( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T);
void GG( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T);
void FF( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T);
DWORD RotateLeft(DWORD x, int n);
char* GetBufferMD5(BYTE* pBuf, UINT nLength,char* MD5Buffer);

BOOL GetFileMd5(LPCTSTR lpszFile, LPTSTR szMd5)
{
    if (NULL == lpszFile || NULL == szMd5)
        return FALSE;

    // This loop handles reading the data.  
    HANDLE hSt = CreateFile(lpszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    if (hSt == INVALID_HANDLE_VALUE)
    {
        return FALSE;
    }
    const int md5chkbufsize = 10240;
    BYTE* lpszbuf = new BYTE[md5chkbufsize];
    if (lpszbuf == NULL)
    {
        CloseHandle(hSt);
        return FALSE;
    }

	SetFilePointer(hSt, 0, 0, FILE_BEGIN);

    TMD5Checksum md5 = {0};
    CMD5ChecksumIni(&md5);
    DWORD dwRead;
    do
    {
        if (ReadFile(hSt, lpszbuf, md5chkbufsize, &dwRead, NULL))
        {
            if (dwRead > 0) 
               Update(&md5, lpszbuf, dwRead);
			if (dwRead < md5chkbufsize)
				break;
        }
        else
        {
            delete lpszbuf;
            CloseHandle(hSt);
            return FALSE;
        }
    }while(dwRead);
    CloseHandle(hSt);
    delete lpszbuf;
	
#ifdef  _UNICODE
    char MD5Buffer[33] = {0};
    Final(&md5, MD5Buffer);
    MultiByteToWideChar(CP_ACP, 0, MD5Buffer, -1, szMd5, 32);
#else
    Final(&md5, szMd5);
#endif
	szMd5[32] = 0; 
	return TRUE;
}
BOOL Md5Encrypt(LPCSTR strPWD, LPSTR szMd5)
{
    if (strPWD == NULL || 0 == strlen(strPWD))
    {
        return FALSE;
    }
    TMD5Checksum MDSBuffer;
    CMD5ChecksumIni(&MDSBuffer);
    Update(&MDSBuffer, (unsigned char *)strPWD, (ULONG)strlen(strPWD));
    Final(&MDSBuffer, szMd5);
    return TRUE;
}

/*****************************************************************************************
FUNCTION:		CMD5Checksum::GetMD5
DETAILS:		static, public
DESCRIPTION:	Gets the MD5 checksum for data in a BYTE array
RETURNS:		CString : the hexadecimal MD5 checksum for the specified data
ARGUMENTS:		BYTE* pBuf  :	pointer to the BYTE array
				UINT nLength :	number of BYTEs of data to be checksumed
NOTES:			Provides an interface to the CMD5Checksum class. Any data that can
				be cast to a BYTE array of known length can be checksummed by this
				function. Typically, CString and char arrays will be checksumed, 
				although this function can be used to check the integrity of any BYTE array. 
				A buffer of zero length can be checksummed; all buffers of zero length 
				will return the same checksum. 
*****************************************************************************************/
char* GetBufferMD5(BYTE* pBuf, UINT nLength,char* MD5Buffer)
{

	TMD5Checksum MD5Checksum;
        //memset(&MD5Checksum,0,sizeof(MD5Checksum));

    CMD5ChecksumIni(&MD5Checksum);
	Update(&MD5Checksum, pBuf, nLength );

	return Final(&MD5Checksum,MD5Buffer);
}


/*****************************************************************************************
FUNCTION:		CMD5Checksum::RotateLeft
DETAILS:		private
DESCRIPTION:	Rotates the bits in a 32 bit DWORD left by a specified amount
RETURNS:		The rotated DWORD 
ARGUMENTS:		DWORD x : the value to be rotated
				int n   : the number of bits to rotate by
*****************************************************************************************/
DWORD RotateLeft(DWORD x, int n)
{
	return (x << n) | (x >> (32-n));
}
/*****************************************************************************************
FUNCTION:		CMD5Checksum::FF
DETAILS:		protected
DESCRIPTION:	Implementation of basic MD5 transformation algorithm
RETURNS:		none
ARGUMENTS:		DWORD &A, B, C, D : Current (partial) checksum
				DWORD X           : Input data
				DWORD S			  : MD5_SXX Transformation constant
				DWORD T			  :	MD5_TXX Transformation constant
NOTES:			None
*****************************************************************************************/
void FF( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T)
{
	DWORD F = (B & C) | (~B & D);
	A += F + X + T;
	A = RotateLeft(A, S);
	A += B;
}


/*****************************************************************************************
FUNCTION:		CMD5Checksum::GG
DETAILS:		protected
DESCRIPTION:	Implementation of basic MD5 transformation algorithm
RETURNS:		none
ARGUMENTS:		DWORD &A, B, C, D : Current (partial) checksum
				DWORD X           : Input data
				DWORD S			  : MD5_SXX Transformation constant
				DWORD T			  :	MD5_TXX Transformation constant
NOTES:			None
*****************************************************************************************/
void GG( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T)
{
	DWORD G = (B & D) | (C & ~D);
	A += G + X + T;
	A = RotateLeft(A, S);
	A += B;
}
/*****************************************************************************************
FUNCTION:		CMD5Checksum::HH
DETAILS:		protected
DESCRIPTION:	Implementation of basic MD5 transformation algorithm
RETURNS:		none
ARGUMENTS:		DWORD &A, B, C, D : Current (partial) checksum
				DWORD X           : Input data
				DWORD S			  : MD5_SXX Transformation constant
				DWORD T			  :	MD5_TXX Transformation constant
NOTES:			None
*****************************************************************************************/
void HH( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T)
{
	DWORD H = (B ^ C ^ D);
	A += H + X + T;
	A = RotateLeft(A, S);
	A += B;
}


/*****************************************************************************************
FUNCTION:		CMD5Checksum::II
DETAILS:		protected
DESCRIPTION:	Implementation of basic MD5 transformation algorithm
RETURNS:		none
ARGUMENTS:		DWORD &A, B, C, D : Current (partial) checksum
				DWORD X           : Input data
				DWORD S			  : MD5_SXX Transformation constant
				DWORD T			  :	MD5_TXX Transformation constant
NOTES:			None
*****************************************************************************************/
void II( DWORD& A, DWORD B, DWORD C, DWORD D, DWORD X, DWORD S, DWORD T)
{
	DWORD I = (C ^ (B | ~D));
	A += I + X + T;
	A = RotateLeft(A, S);
	A += B;
}


/*****************************************************************************************
FUNCTION:		CMD5Checksum::ByteToDWord
DETAILS:		private
DESCRIPTION:	Transfers the data in an 8 bit array to a 32 bit array
RETURNS:		void
ARGUMENTS:		DWORD* Output : the 32 bit (unsigned long) destination array 
				BYTE* Input	  : the 8 bit (unsigned char) source array
				UINT nLength  : the number of 8 bit data items in the source array
NOTES:			Four BYTES from the input array are transferred to each DWORD entry
				of the output array. The first BYTE is transferred to the bits (0-7) 
				of the output DWORD, the second BYTE to bits 8-15 etc. 
				The algorithm assumes that the input array is a multiple of 4 bytes long
				so that there is a perfect fit into the array of 32 bit words.
*****************************************************************************************/
void ByteToDWord(DWORD* Output, BYTE* Input, UINT nLength)
{
        if(Output==NULL||Input==NULL)return;
	UINT i=0;	//index to Output array
	UINT j=0;	//index to Input array

	//transfer the data by shifting and copying
	for ( ; j < nLength; i++, j += 4)
	{
		Output[i] = (ULONG)Input[j]			| 
					(ULONG)Input[j+1] << 8	| 
					(ULONG)Input[j+2] << 16 | 
					(ULONG)Input[j+3] << 24;
	}
}

/*****************************************************************************************
FUNCTION:		CMD5Checksum::Transform
DETAILS:		protected
DESCRIPTION:	MD5 basic transformation algorithm;  transforms 'This->m_lMD5'
RETURNS:		void
ARGUMENTS:		BYTE Block[64]
NOTES:			An MD5 checksum is calculated by four rounds of 'Transformation'.
				The MD5 checksum currently held in This->m_lMD5 is merged by the 
				transformation process with data passed in 'Block'.  
*****************************************************************************************/
void Transform(PMD5Checksum This,BYTE Block[64])
{
	//initialise local data with current checksum
	ULONG a = This->m_lMD5[0];
	ULONG b = This->m_lMD5[1];
	ULONG c = This->m_lMD5[2];
	ULONG d = This->m_lMD5[3];

	//copy BYTES from input 'Block' to an array of ULONGS 'X'
	ULONG X[16];
	ByteToDWord( X, Block, 64 );

	//Perform Round 1 of the transformation
	FF (a, b, c, d, X[ 0], MD5_S11, MD5_T01); 
	FF (d, a, b, c, X[ 1], MD5_S12, MD5_T02); 
	FF (c, d, a, b, X[ 2], MD5_S13, MD5_T03); 
	FF (b, c, d, a, X[ 3], MD5_S14, MD5_T04); 
	FF (a, b, c, d, X[ 4], MD5_S11, MD5_T05); 
	FF (d, a, b, c, X[ 5], MD5_S12, MD5_T06); 
	FF (c, d, a, b, X[ 6], MD5_S13, MD5_T07); 
	FF (b, c, d, a, X[ 7], MD5_S14, MD5_T08); 
	FF (a, b, c, d, X[ 8], MD5_S11, MD5_T09); 
	FF (d, a, b, c, X[ 9], MD5_S12, MD5_T10); 
	FF (c, d, a, b, X[10], MD5_S13, MD5_T11); 
	FF (b, c, d, a, X[11], MD5_S14, MD5_T12); 
	FF (a, b, c, d, X[12], MD5_S11, MD5_T13); 
	FF (d, a, b, c, X[13], MD5_S12, MD5_T14); 
	FF (c, d, a, b, X[14], MD5_S13, MD5_T15); 
	FF (b, c, d, a, X[15], MD5_S14, MD5_T16); 

	//Perform Round 2 of the transformation
	GG (a, b, c, d, X[ 1], MD5_S21, MD5_T17); 
	GG (d, a, b, c, X[ 6], MD5_S22, MD5_T18); 
	GG (c, d, a, b, X[11], MD5_S23, MD5_T19); 
	GG (b, c, d, a, X[ 0], MD5_S24, MD5_T20); 
	GG (a, b, c, d, X[ 5], MD5_S21, MD5_T21); 
	GG (d, a, b, c, X[10], MD5_S22, MD5_T22); 
	GG (c, d, a, b, X[15], MD5_S23, MD5_T23); 
	GG (b, c, d, a, X[ 4], MD5_S24, MD5_T24); 
	GG (a, b, c, d, X[ 9], MD5_S21, MD5_T25); 
	GG (d, a, b, c, X[14], MD5_S22, MD5_T26); 
	GG (c, d, a, b, X[ 3], MD5_S23, MD5_T27); 
	GG (b, c, d, a, X[ 8], MD5_S24, MD5_T28); 
	GG (a, b, c, d, X[13], MD5_S21, MD5_T29); 
	GG (d, a, b, c, X[ 2], MD5_S22, MD5_T30); 
	GG (c, d, a, b, X[ 7], MD5_S23, MD5_T31); 
	GG (b, c, d, a, X[12], MD5_S24, MD5_T32); 

	//Perform Round 3 of the transformation
	HH (a, b, c, d, X[ 5], MD5_S31, MD5_T33); 
	HH (d, a, b, c, X[ 8], MD5_S32, MD5_T34); 
	HH (c, d, a, b, X[11], MD5_S33, MD5_T35); 
	HH (b, c, d, a, X[14], MD5_S34, MD5_T36); 
	HH (a, b, c, d, X[ 1], MD5_S31, MD5_T37); 
	HH (d, a, b, c, X[ 4], MD5_S32, MD5_T38); 
	HH (c, d, a, b, X[ 7], MD5_S33, MD5_T39); 
	HH (b, c, d, a, X[10], MD5_S34, MD5_T40); 
	HH (a, b, c, d, X[13], MD5_S31, MD5_T41); 
	HH (d, a, b, c, X[ 0], MD5_S32, MD5_T42); 
	HH (c, d, a, b, X[ 3], MD5_S33, MD5_T43); 
	HH (b, c, d, a, X[ 6], MD5_S34, MD5_T44); 
	HH (a, b, c, d, X[ 9], MD5_S31, MD5_T45); 
	HH (d, a, b, c, X[12], MD5_S32, MD5_T46); 
	HH (c, d, a, b, X[15], MD5_S33, MD5_T47); 
	HH (b, c, d, a, X[ 2], MD5_S34, MD5_T48); 

	//Perform Round 4 of the transformation
	II (a, b, c, d, X[ 0], MD5_S41, MD5_T49); 
	II (d, a, b, c, X[ 7], MD5_S42, MD5_T50); 
	II (c, d, a, b, X[14], MD5_S43, MD5_T51); 
	II (b, c, d, a, X[ 5], MD5_S44, MD5_T52); 
	II (a, b, c, d, X[12], MD5_S41, MD5_T53); 
	II (d, a, b, c, X[ 3], MD5_S42, MD5_T54); 
	II (c, d, a, b, X[10], MD5_S43, MD5_T55); 
	II (b, c, d, a, X[ 1], MD5_S44, MD5_T56); 
	II (a, b, c, d, X[ 8], MD5_S41, MD5_T57); 
	II (d, a, b, c, X[15], MD5_S42, MD5_T58); 
	II (c, d, a, b, X[ 6], MD5_S43, MD5_T59); 
	II (b, c, d, a, X[13], MD5_S44, MD5_T60); 
	II (a, b, c, d, X[ 4], MD5_S41, MD5_T61); 
	II (d, a, b, c, X[11], MD5_S42, MD5_T62); 
	II (c, d, a, b, X[ 2], MD5_S43, MD5_T63); 
	II (b, c, d, a, X[ 9], MD5_S44, MD5_T64); 

	//add the transformed values to the current checksum
	This->m_lMD5[0] += a;
	This->m_lMD5[1] += b;
	This->m_lMD5[2] += c;
	This->m_lMD5[3] += d;
}


/*****************************************************************************************
CONSTRUCTOR:	CMD5Checksum
DESCRIPTION:	Initialises member data
ARGUMENTS:		None
NOTES:			None
*****************************************************************************************/
void CMD5ChecksumIni(PMD5Checksum This)
{
	// zero members
	memset(This->m_lpszBuffer, 0, 64 );
	This->m_nCount[0] = This->m_nCount[1] = 0;

	// Load magic state initialization constants
	This->m_lMD5[0] = MD5_INIT_STATE_0;
	This->m_lMD5[1] = MD5_INIT_STATE_1;
	This->m_lMD5[2] = MD5_INIT_STATE_2;
	This->m_lMD5[3] = MD5_INIT_STATE_3;
}

/*****************************************************************************************
FUNCTION:		CMD5Checksum::DWordToByte
DETAILS:		private
DESCRIPTION:	Transfers the data in an 32 bit array to a 8 bit array
RETURNS:		void
ARGUMENTS:		BYTE* Output  : the 8 bit destination array 
				DWORD* Input  : the 32 bit source array
				UINT nLength  : the number of 8 bit data items in the source array
NOTES:			One DWORD from the input array is transferred into four BYTES 
				in the output array. The first (0-7) bits of the first DWORD are 
				transferred to the first output BYTE, bits bits 8-15 are transferred from
				the second BYTE etc. 

				The algorithm assumes that the output array is a multiple of 4 bytes long
				so that there is a perfect fit of 8 bit BYTES into the 32 bit DWORDs.
*****************************************************************************************/
void  DWordToByte(BYTE* Output, DWORD* Input, UINT nLength )
{
        if(Output==NULL||Input==NULL)return;

	//transfer the data by shifting and copying
	UINT i = 0;
	UINT j = 0;
	for ( ; j < nLength; i++, j += 4) 
	{
		Output[j] =   (UCHAR)(Input[i] & 0xff);
		Output[j+1] = (UCHAR)((Input[i] >> 8) & 0xff);
		Output[j+2] = (UCHAR)((Input[i] >> 16) & 0xff);
		Output[j+3] = (UCHAR)((Input[i] >> 24) & 0xff);
	}
}


/*****************************************************************************************
FUNCTION:		CMD5Checksum::Final
DETAILS:		protected
DESCRIPTION:	Implementation of main MD5 checksum algorithm; ends the checksum calculation.
RETURNS:		CString : the final hexadecimal MD5 checksum result 
ARGUMENTS:		None
NOTES:			Performs the final MD5 checksum calculation ('Update' does most of the work,
				this function just finishes the calculation.) 
*****************************************************************************************/
char* Final(PMD5Checksum This,char* Buff)
{
	//Save number of bits
	BYTE Bits[8];
	DWordToByte( Bits, This->m_nCount, 8 );

	//Pad out to 56 mod 64.
	UINT nIndex = (UINT)((This->m_nCount[0] >> 3) & 0x3f);
	UINT nPadLen = (nIndex < 56) ? (56 - nIndex) : (120 - nIndex);
	Update(This, PADDING, nPadLen );

	//Append length (before padding)
	Update(This, Bits, 8 );

	//Store final state in 'lpszMD5'
	const int nMD5Size = 16;
	unsigned char lpszMD5[ nMD5Size ];
	DWordToByte( lpszMD5, This->m_lMD5, nMD5Size );

	//Convert the hexadecimal checksum to a CString
        int j=0;
        char p;
	for ( int i=0; i < nMD5Size; i++)
	{
                p=lpszMD5[i];
                p=0x0F&(p>>4);
                Buff[j++]=p+48+(p>9)*7;
                p=lpszMD5[i];
                p=0x0F&(p);
                Buff[j++]=p+48+(p>9)*7;
	}
	return Buff;
}


/*****************************************************************************************
FUNCTION:		CMD5Checksum::Update
DETAILS:		protected
DESCRIPTION:	Implementation of main MD5 checksum algorithm
RETURNS:		void
ARGUMENTS:		BYTE* Input    : input block
				UINT nInputLen : length of input block
NOTES:			Computes the partial MD5 checksum for 'nInputLen' bytes of data in 'Input'
*****************************************************************************************/
void Update(PMD5Checksum This,BYTE* Input,	ULONG nInputLen )
{
	//Compute number of bytes mod 64
	UINT nIndex = (UINT)((This->m_nCount[0] >> 3) & 0x3F);

	//Update number of bits
	if ( ( This->m_nCount[0] += nInputLen << 3 )  <  ( nInputLen << 3) )
	{
		This->m_nCount[1]++;
	}
	This->m_nCount[1] += (nInputLen >> 29);

	//Transform as many times as possible.
	UINT i=0;		
	UINT nPartLen = 64 - nIndex;
	if (nInputLen >= nPartLen) 	
	{
		memcpy( &This->m_lpszBuffer[nIndex], Input, nPartLen );
		Transform(This,This->m_lpszBuffer );
		for (i = nPartLen; i + 63 < nInputLen; i += 64) 
		{
			Transform(This,&Input[i] );
		}
		nIndex = 0;
	} 
	else 
	{
		i = 0;
	}
	memcpy( &This->m_lpszBuffer[nIndex], &Input[i], nInputLen-i);
}